From 75d79abf9801195a0241b02ed7974fafa8278f5c Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Tue, 27 Feb 2001 20:40:15 +0000 Subject: [PATCH] test the window state stuff 2001-02-26 Havoc Pennington * gtk/testgtk.c: test the window state stuff * gtk/gtkwindow.c (gtk_window_present): new function, makes a window come to the user's attention as if it were just created (gtk_window_iconify): new function (gtk_window_deiconify): new function (gtk_window_stick): new function (gtk_window_unstick): new function (gtk_window_maximize): new function (gtk_window_unmaximize): new function * gtk/gtkwidget.h, gtk/gtkwidget.c: add window_state_event signal * gtk/gtkmain.c (gtk_main_do_event): handle GDK_WINDOW_STATE * gdk/x11/gdkevents-x11.c: create window state events when appropriate (gdk_wmspec_supported): new function * gdk/x11/gdkwindow-x11.c (gdk_window_iconify): handle iconification before showing the window (gdk_window_deiconify): new function (gdk_window_stick): new function (gdk_window_unstick): new function (gdk_window_maximize): new function (gdk_window_unmaximize): new function * gdk/gdkwindow.c: store the window state in the window; change to using the GDK_WINDOW_STATE_WITHDRAWN flag instead of window->mapped. (gdk_window_get_state): return the current window state * gdk/gdkevents.c (gdk_event_get_time): handle GDK_WINDOW_STATE (gdk_event_get_state): handle GDK_WINDOW_STATE (gdk_synthesize_window_state): function to create the window state events * gdk/gdkevents.h (struct _GdkEventWindowState): new type of event, for changes to "window state" such as maximized, sticky, etc. * gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function, focuses a window * gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function, finds out if we support a given WM spec hint --- ChangeLog | 50 ++++- ChangeLog.pre-2-0 | 50 ++++- ChangeLog.pre-2-10 | 50 ++++- ChangeLog.pre-2-2 | 50 ++++- ChangeLog.pre-2-4 | 50 ++++- ChangeLog.pre-2-6 | 50 ++++- ChangeLog.pre-2-8 | 50 ++++- gdk/gdkevents.c | 55 +++++ gdk/gdkevents.h | 24 ++- gdk/gdkinternals.h | 5 + gdk/gdkwindow.c | 34 +++- gdk/gdkwindow.h | 14 +- gdk/x11/gdkevents-x11.c | 263 +++++++++++++++++++++++- gdk/x11/gdkgeometry-x11.c | 7 +- gdk/x11/gdkmain-x11.c | 1 - gdk/x11/gdkwindow-x11.c | 413 +++++++++++++++++++++++++++++++++++++- gdk/x11/gdkx.h | 3 + gtk/gtkmain.c | 1 + gtk/gtkwidget.c | 13 ++ gtk/gtkwidget.h | 4 +- gtk/gtkwindow.c | 303 +++++++++++++++++++++++++++- gtk/gtkwindow.h | 13 ++ gtk/testgtk.c | 194 ++++++++++++++++++ tests/testgtk.c | 194 ++++++++++++++++++ 24 files changed, 1853 insertions(+), 38 deletions(-) diff --git a/ChangeLog b/ChangeLog index a613f1cde1..749c20c2d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,52 @@ +2001-02-26 Havoc Pennington + + * gtk/testgtk.c: test the window state stuff + + * gtk/gtkwindow.c (gtk_window_present): new function, makes a + window come to the user's attention as if it were just created + (gtk_window_iconify): new function + (gtk_window_deiconify): new function + (gtk_window_stick): new function + (gtk_window_unstick): new function + (gtk_window_maximize): new function + (gtk_window_unmaximize): new function + + * gtk/gtkwidget.h, gtk/gtkwidget.c: add window_state_event signal + + * gtk/gtkmain.c (gtk_main_do_event): handle GDK_WINDOW_STATE + + * gdk/x11/gdkevents-x11.c: create window state events when + appropriate + (gdk_wmspec_supported): new function + + * gdk/x11/gdkwindow-x11.c (gdk_window_iconify): handle + iconification before showing the window + (gdk_window_deiconify): new function + (gdk_window_stick): new function + (gdk_window_unstick): new function + (gdk_window_maximize): new function + (gdk_window_unmaximize): new function + + * gdk/gdkwindow.c: store the window state in the window; + change to using the GDK_WINDOW_STATE_WITHDRAWN flag instead of + window->mapped. + (gdk_window_get_state): return the current window state + + * gdk/gdkevents.c (gdk_event_get_time): handle GDK_WINDOW_STATE + (gdk_event_get_state): handle GDK_WINDOW_STATE + (gdk_synthesize_window_state): function to create the window state + events + + * gdk/gdkevents.h (struct _GdkEventWindowState): new type of + event, for changes to "window state" such as maximized, sticky, + etc. + + * gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function, + focuses a window + + * gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function, + finds out if we support a given WM spec hint + Tue Feb 27 13:37:38 GMT 2001 Tony Gale * docs/faq/gtk-faq.sgml: New question re memory leaks. @@ -24,7 +73,6 @@ Sat Feb 24 20:11:39 2001 Jonathan Blandford * gtk/gtkclipboard.h: actually put /* */ around #endif comment. - Fri Feb 23 17:50:13 2001 Jonathan Blandford * gtk/gtktreeview.c (gtk_tree_view_bin_expose): Only draw the diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index a613f1cde1..749c20c2d9 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,52 @@ +2001-02-26 Havoc Pennington + + * gtk/testgtk.c: test the window state stuff + + * gtk/gtkwindow.c (gtk_window_present): new function, makes a + window come to the user's attention as if it were just created + (gtk_window_iconify): new function + (gtk_window_deiconify): new function + (gtk_window_stick): new function + (gtk_window_unstick): new function + (gtk_window_maximize): new function + (gtk_window_unmaximize): new function + + * gtk/gtkwidget.h, gtk/gtkwidget.c: add window_state_event signal + + * gtk/gtkmain.c (gtk_main_do_event): handle GDK_WINDOW_STATE + + * gdk/x11/gdkevents-x11.c: create window state events when + appropriate + (gdk_wmspec_supported): new function + + * gdk/x11/gdkwindow-x11.c (gdk_window_iconify): handle + iconification before showing the window + (gdk_window_deiconify): new function + (gdk_window_stick): new function + (gdk_window_unstick): new function + (gdk_window_maximize): new function + (gdk_window_unmaximize): new function + + * gdk/gdkwindow.c: store the window state in the window; + change to using the GDK_WINDOW_STATE_WITHDRAWN flag instead of + window->mapped. + (gdk_window_get_state): return the current window state + + * gdk/gdkevents.c (gdk_event_get_time): handle GDK_WINDOW_STATE + (gdk_event_get_state): handle GDK_WINDOW_STATE + (gdk_synthesize_window_state): function to create the window state + events + + * gdk/gdkevents.h (struct _GdkEventWindowState): new type of + event, for changes to "window state" such as maximized, sticky, + etc. + + * gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function, + focuses a window + + * gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function, + finds out if we support a given WM spec hint + Tue Feb 27 13:37:38 GMT 2001 Tony Gale * docs/faq/gtk-faq.sgml: New question re memory leaks. @@ -24,7 +73,6 @@ Sat Feb 24 20:11:39 2001 Jonathan Blandford * gtk/gtkclipboard.h: actually put /* */ around #endif comment. - Fri Feb 23 17:50:13 2001 Jonathan Blandford * gtk/gtktreeview.c (gtk_tree_view_bin_expose): Only draw the diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index a613f1cde1..749c20c2d9 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,52 @@ +2001-02-26 Havoc Pennington + + * gtk/testgtk.c: test the window state stuff + + * gtk/gtkwindow.c (gtk_window_present): new function, makes a + window come to the user's attention as if it were just created + (gtk_window_iconify): new function + (gtk_window_deiconify): new function + (gtk_window_stick): new function + (gtk_window_unstick): new function + (gtk_window_maximize): new function + (gtk_window_unmaximize): new function + + * gtk/gtkwidget.h, gtk/gtkwidget.c: add window_state_event signal + + * gtk/gtkmain.c (gtk_main_do_event): handle GDK_WINDOW_STATE + + * gdk/x11/gdkevents-x11.c: create window state events when + appropriate + (gdk_wmspec_supported): new function + + * gdk/x11/gdkwindow-x11.c (gdk_window_iconify): handle + iconification before showing the window + (gdk_window_deiconify): new function + (gdk_window_stick): new function + (gdk_window_unstick): new function + (gdk_window_maximize): new function + (gdk_window_unmaximize): new function + + * gdk/gdkwindow.c: store the window state in the window; + change to using the GDK_WINDOW_STATE_WITHDRAWN flag instead of + window->mapped. + (gdk_window_get_state): return the current window state + + * gdk/gdkevents.c (gdk_event_get_time): handle GDK_WINDOW_STATE + (gdk_event_get_state): handle GDK_WINDOW_STATE + (gdk_synthesize_window_state): function to create the window state + events + + * gdk/gdkevents.h (struct _GdkEventWindowState): new type of + event, for changes to "window state" such as maximized, sticky, + etc. + + * gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function, + focuses a window + + * gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function, + finds out if we support a given WM spec hint + Tue Feb 27 13:37:38 GMT 2001 Tony Gale * docs/faq/gtk-faq.sgml: New question re memory leaks. @@ -24,7 +73,6 @@ Sat Feb 24 20:11:39 2001 Jonathan Blandford * gtk/gtkclipboard.h: actually put /* */ around #endif comment. - Fri Feb 23 17:50:13 2001 Jonathan Blandford * gtk/gtktreeview.c (gtk_tree_view_bin_expose): Only draw the diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index a613f1cde1..749c20c2d9 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,52 @@ +2001-02-26 Havoc Pennington + + * gtk/testgtk.c: test the window state stuff + + * gtk/gtkwindow.c (gtk_window_present): new function, makes a + window come to the user's attention as if it were just created + (gtk_window_iconify): new function + (gtk_window_deiconify): new function + (gtk_window_stick): new function + (gtk_window_unstick): new function + (gtk_window_maximize): new function + (gtk_window_unmaximize): new function + + * gtk/gtkwidget.h, gtk/gtkwidget.c: add window_state_event signal + + * gtk/gtkmain.c (gtk_main_do_event): handle GDK_WINDOW_STATE + + * gdk/x11/gdkevents-x11.c: create window state events when + appropriate + (gdk_wmspec_supported): new function + + * gdk/x11/gdkwindow-x11.c (gdk_window_iconify): handle + iconification before showing the window + (gdk_window_deiconify): new function + (gdk_window_stick): new function + (gdk_window_unstick): new function + (gdk_window_maximize): new function + (gdk_window_unmaximize): new function + + * gdk/gdkwindow.c: store the window state in the window; + change to using the GDK_WINDOW_STATE_WITHDRAWN flag instead of + window->mapped. + (gdk_window_get_state): return the current window state + + * gdk/gdkevents.c (gdk_event_get_time): handle GDK_WINDOW_STATE + (gdk_event_get_state): handle GDK_WINDOW_STATE + (gdk_synthesize_window_state): function to create the window state + events + + * gdk/gdkevents.h (struct _GdkEventWindowState): new type of + event, for changes to "window state" such as maximized, sticky, + etc. + + * gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function, + focuses a window + + * gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function, + finds out if we support a given WM spec hint + Tue Feb 27 13:37:38 GMT 2001 Tony Gale * docs/faq/gtk-faq.sgml: New question re memory leaks. @@ -24,7 +73,6 @@ Sat Feb 24 20:11:39 2001 Jonathan Blandford * gtk/gtkclipboard.h: actually put /* */ around #endif comment. - Fri Feb 23 17:50:13 2001 Jonathan Blandford * gtk/gtktreeview.c (gtk_tree_view_bin_expose): Only draw the diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index a613f1cde1..749c20c2d9 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,52 @@ +2001-02-26 Havoc Pennington + + * gtk/testgtk.c: test the window state stuff + + * gtk/gtkwindow.c (gtk_window_present): new function, makes a + window come to the user's attention as if it were just created + (gtk_window_iconify): new function + (gtk_window_deiconify): new function + (gtk_window_stick): new function + (gtk_window_unstick): new function + (gtk_window_maximize): new function + (gtk_window_unmaximize): new function + + * gtk/gtkwidget.h, gtk/gtkwidget.c: add window_state_event signal + + * gtk/gtkmain.c (gtk_main_do_event): handle GDK_WINDOW_STATE + + * gdk/x11/gdkevents-x11.c: create window state events when + appropriate + (gdk_wmspec_supported): new function + + * gdk/x11/gdkwindow-x11.c (gdk_window_iconify): handle + iconification before showing the window + (gdk_window_deiconify): new function + (gdk_window_stick): new function + (gdk_window_unstick): new function + (gdk_window_maximize): new function + (gdk_window_unmaximize): new function + + * gdk/gdkwindow.c: store the window state in the window; + change to using the GDK_WINDOW_STATE_WITHDRAWN flag instead of + window->mapped. + (gdk_window_get_state): return the current window state + + * gdk/gdkevents.c (gdk_event_get_time): handle GDK_WINDOW_STATE + (gdk_event_get_state): handle GDK_WINDOW_STATE + (gdk_synthesize_window_state): function to create the window state + events + + * gdk/gdkevents.h (struct _GdkEventWindowState): new type of + event, for changes to "window state" such as maximized, sticky, + etc. + + * gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function, + focuses a window + + * gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function, + finds out if we support a given WM spec hint + Tue Feb 27 13:37:38 GMT 2001 Tony Gale * docs/faq/gtk-faq.sgml: New question re memory leaks. @@ -24,7 +73,6 @@ Sat Feb 24 20:11:39 2001 Jonathan Blandford * gtk/gtkclipboard.h: actually put /* */ around #endif comment. - Fri Feb 23 17:50:13 2001 Jonathan Blandford * gtk/gtktreeview.c (gtk_tree_view_bin_expose): Only draw the diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index a613f1cde1..749c20c2d9 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,52 @@ +2001-02-26 Havoc Pennington + + * gtk/testgtk.c: test the window state stuff + + * gtk/gtkwindow.c (gtk_window_present): new function, makes a + window come to the user's attention as if it were just created + (gtk_window_iconify): new function + (gtk_window_deiconify): new function + (gtk_window_stick): new function + (gtk_window_unstick): new function + (gtk_window_maximize): new function + (gtk_window_unmaximize): new function + + * gtk/gtkwidget.h, gtk/gtkwidget.c: add window_state_event signal + + * gtk/gtkmain.c (gtk_main_do_event): handle GDK_WINDOW_STATE + + * gdk/x11/gdkevents-x11.c: create window state events when + appropriate + (gdk_wmspec_supported): new function + + * gdk/x11/gdkwindow-x11.c (gdk_window_iconify): handle + iconification before showing the window + (gdk_window_deiconify): new function + (gdk_window_stick): new function + (gdk_window_unstick): new function + (gdk_window_maximize): new function + (gdk_window_unmaximize): new function + + * gdk/gdkwindow.c: store the window state in the window; + change to using the GDK_WINDOW_STATE_WITHDRAWN flag instead of + window->mapped. + (gdk_window_get_state): return the current window state + + * gdk/gdkevents.c (gdk_event_get_time): handle GDK_WINDOW_STATE + (gdk_event_get_state): handle GDK_WINDOW_STATE + (gdk_synthesize_window_state): function to create the window state + events + + * gdk/gdkevents.h (struct _GdkEventWindowState): new type of + event, for changes to "window state" such as maximized, sticky, + etc. + + * gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function, + focuses a window + + * gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function, + finds out if we support a given WM spec hint + Tue Feb 27 13:37:38 GMT 2001 Tony Gale * docs/faq/gtk-faq.sgml: New question re memory leaks. @@ -24,7 +73,6 @@ Sat Feb 24 20:11:39 2001 Jonathan Blandford * gtk/gtkclipboard.h: actually put /* */ around #endif comment. - Fri Feb 23 17:50:13 2001 Jonathan Blandford * gtk/gtktreeview.c (gtk_tree_view_bin_expose): Only draw the diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index a613f1cde1..749c20c2d9 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,52 @@ +2001-02-26 Havoc Pennington + + * gtk/testgtk.c: test the window state stuff + + * gtk/gtkwindow.c (gtk_window_present): new function, makes a + window come to the user's attention as if it were just created + (gtk_window_iconify): new function + (gtk_window_deiconify): new function + (gtk_window_stick): new function + (gtk_window_unstick): new function + (gtk_window_maximize): new function + (gtk_window_unmaximize): new function + + * gtk/gtkwidget.h, gtk/gtkwidget.c: add window_state_event signal + + * gtk/gtkmain.c (gtk_main_do_event): handle GDK_WINDOW_STATE + + * gdk/x11/gdkevents-x11.c: create window state events when + appropriate + (gdk_wmspec_supported): new function + + * gdk/x11/gdkwindow-x11.c (gdk_window_iconify): handle + iconification before showing the window + (gdk_window_deiconify): new function + (gdk_window_stick): new function + (gdk_window_unstick): new function + (gdk_window_maximize): new function + (gdk_window_unmaximize): new function + + * gdk/gdkwindow.c: store the window state in the window; + change to using the GDK_WINDOW_STATE_WITHDRAWN flag instead of + window->mapped. + (gdk_window_get_state): return the current window state + + * gdk/gdkevents.c (gdk_event_get_time): handle GDK_WINDOW_STATE + (gdk_event_get_state): handle GDK_WINDOW_STATE + (gdk_synthesize_window_state): function to create the window state + events + + * gdk/gdkevents.h (struct _GdkEventWindowState): new type of + event, for changes to "window state" such as maximized, sticky, + etc. + + * gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function, + focuses a window + + * gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function, + finds out if we support a given WM spec hint + Tue Feb 27 13:37:38 GMT 2001 Tony Gale * docs/faq/gtk-faq.sgml: New question re memory leaks. @@ -24,7 +73,6 @@ Sat Feb 24 20:11:39 2001 Jonathan Blandford * gtk/gtkclipboard.h: actually put /* */ around #endif comment. - Fri Feb 23 17:50:13 2001 Jonathan Blandford * gtk/gtktreeview.c (gtk_tree_view_bin_expose): Only draw the diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 8308bbeccd..203f7f4e24 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -454,6 +454,7 @@ gdk_event_get_time (GdkEvent *event) case GDK_EXPOSE: case GDK_MAP: case GDK_UNMAP: + case GDK_WINDOW_STATE: /* return current time */ break; } @@ -529,6 +530,7 @@ gdk_event_get_state (GdkEvent *event, case GDK_EXPOSE: case GDK_MAP: case GDK_UNMAP: + case GDK_WINDOW_STATE: /* no state field */ break; } @@ -803,3 +805,56 @@ gdk_event_button_generate (GdkEvent *event) button_number[0] = event->button.button; } } + + +void +gdk_synthesize_window_state (GdkWindow *window, + GdkWindowState unset_flags, + GdkWindowState set_flags) +{ + GdkEventWindowState temp_event; + GdkWindowState old; + + g_return_if_fail (window != NULL); + + temp_event.window = window; + temp_event.type = GDK_WINDOW_STATE; + temp_event.send_event = FALSE; + + old = ((GdkWindowObject*) temp_event.window)->state; + + temp_event.changed_mask = (unset_flags | set_flags) ^ old; + temp_event.new_window_state = old; + temp_event.new_window_state |= set_flags; + temp_event.new_window_state &= ~unset_flags; + + if (temp_event.new_window_state == old) + return; /* No actual work to do, nothing changed. */ + + /* Actually update the field in GdkWindow, this is sort of an odd + * place to do it, but seems like the safest since it ensures we expose no + * inconsistent state to the user. + */ + + ((GdkWindowObject*) window)->state = temp_event.new_window_state; + + /* We only really send the event to toplevels, since + * all the window states don't apply to non-toplevels. + * Non-toplevels do use the GDK_WINDOW_STATE_WITHDRAWN flag + * internally so we needed to update window->state. + */ + switch (((GdkWindowObject*) window)->window_type) + { + case GDK_WINDOW_TOPLEVEL: + case GDK_WINDOW_DIALOG: + case GDK_WINDOW_TEMP: /* ? */ + gdk_event_put ((GdkEvent*) &temp_event); + break; + + case GDK_WINDOW_FOREIGN: + case GDK_WINDOW_ROOT: + case GDK_WINDOW_CHILD: + break; + } +} + diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h index f49b156ecd..ea2c25b3ab 100644 --- a/gdk/gdkevents.h +++ b/gdk/gdkevents.h @@ -28,8 +28,8 @@ typedef struct _GdkEventProperty GdkEventProperty; typedef struct _GdkEventSelection GdkEventSelection; typedef struct _GdkEventProximity GdkEventProximity; typedef struct _GdkEventClient GdkEventClient; - typedef struct _GdkEventDND GdkEventDND; +typedef struct _GdkEventWindowState GdkEventWindowState; typedef union _GdkEvent GdkEvent; @@ -110,7 +110,8 @@ typedef enum GDK_CLIENT_EVENT = 28, GDK_VISIBILITY_NOTIFY = 29, GDK_NO_EXPOSE = 30, - GDK_SCROLL = 31 + GDK_SCROLL = 31, + GDK_WINDOW_STATE = 32 } GdkEventType; /* Event masks. (Used to select what types of events a window @@ -193,6 +194,14 @@ typedef enum GDK_PROPERTY_DELETE } GdkPropertyState; +typedef enum +{ + GDK_WINDOW_STATE_WITHDRAWN = 1 << 0, + GDK_WINDOW_STATE_ICONIFIED = 1 << 1, + GDK_WINDOW_STATE_MAXIMIZED = 1 << 2, + GDK_WINDOW_STATE_STICKY = 1 << 3 +} GdkWindowState; + struct _GdkEventAny { GdkEventType type; @@ -366,6 +375,16 @@ struct _GdkEventClient } data; }; + +struct _GdkEventWindowState +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkWindowState changed_mask; + GdkWindowState new_window_state; +}; + /* Event types for DND */ struct _GdkEventDND { @@ -397,6 +416,7 @@ union _GdkEvent GdkEventProximity proximity; GdkEventClient client; GdkEventDND dnd; + GdkEventWindowState window_state; }; gboolean gdk_events_pending (void); diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 2987f81863..5f96ffd7ed 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -117,6 +117,9 @@ void gdk_event_queue_remove_link (GList *node); void gdk_event_queue_append (GdkEvent *event); void gdk_event_button_generate (GdkEvent *event); +void gdk_synthesize_window_state (GdkWindow *window, + GdkWindowState unset_flags, + GdkWindowState set_flags); /************************************* * Interfaces used by windowing code * @@ -163,6 +166,8 @@ void _gdk_windowing_window_clear_area_e (GdkWindow *window, gint width, gint height); +#define GDK_WINDOW_IS_MAPPED(window) ((((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_WITHDRAWN) == 0) + /* Called before processing updates for a window. This gives the windowing * layer a chance to save the region for later use in avoiding duplicate * exposes. The return value indicates whether the function has a saved diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 6cd615333b..acead1b234 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -188,6 +188,8 @@ gdk_window_init (GdkWindowObject *window) window->window_type = GDK_WINDOW_CHILD; + window->state = GDK_WINDOW_STATE_WITHDRAWN; + window->impl = g_object_new (_gdk_window_impl_get_type (), NULL); } @@ -290,7 +292,7 @@ _gdk_window_destroy_hierarchy (GdkWindow *window, case GDK_WINDOW_FOREIGN: if (!GDK_WINDOW_DESTROYED (window)) { - private->mapped = FALSE; + private->state |= GDK_WINDOW_STATE_WITHDRAWN; private->destroyed = TRUE; _gdk_windowing_window_destroy (window, recursing, foreign_destroy); @@ -573,12 +575,9 @@ gdk_window_get_toplevels (void) gboolean gdk_window_is_visible (GdkWindow *window) { - GdkWindowObject *private = (GdkWindowObject *)window; - - g_return_val_if_fail (window != NULL, FALSE); g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); - return private->mapped; + return GDK_WINDOW_IS_MAPPED (window); } /************************************************************* @@ -605,7 +604,7 @@ gdk_window_is_viewable (GdkWindow *window) (private != (GdkWindowObject *)gdk_parent_root) && (GDK_WINDOW_TYPE (private) != GDK_WINDOW_FOREIGN)) { - if (!private->mapped) + if (!GDK_WINDOW_IS_MAPPED (window)) return FALSE; private = (GdkWindowObject *)private->parent; @@ -614,6 +613,25 @@ gdk_window_is_viewable (GdkWindow *window) return TRUE; } +/** + * gdk_window_get_state: + * @window: a #GdkWindow + * + * Gets the bitwise OR of the currently active window state flags, + * from the #GdkWindowState enumeration. + * + * Return value: window state bitfield + **/ +GdkWindowState +gdk_window_get_state (GdkWindow *window) +{ + GdkWindowObject *private = (GdkWindowObject *)window; + + g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); + + return private->state; +} + /** * gdk_window_begin_paint_rect: * @window: a #GdkWindow @@ -1871,7 +1889,7 @@ gdk_window_invalidate_rect (GdkWindow *window, if (GDK_WINDOW_DESTROYED (window)) return; - if (private->input_only || !private->mapped) + if (private->input_only || !GDK_WINDOW_IS_MAPPED (window)) return; if (!rect) @@ -1927,7 +1945,7 @@ gdk_window_invalidate_region (GdkWindow *window, if (GDK_WINDOW_DESTROYED (window)) return; - if (private->input_only || !private->mapped) + if (private->input_only || !GDK_WINDOW_IS_MAPPED (window)) return; visible_region = gdk_drawable_get_visible_region (window); diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index 435e5868dc..9976265edc 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -199,7 +199,9 @@ struct _GdkWindowObject guint8 window_type; guint8 depth; guint8 resize_count; - guint mapped : 1; + + GdkWindowState state; + guint guffaw_gravity : 1; guint input_only : 1; @@ -254,6 +256,8 @@ void gdk_window_clear_area_e (GdkWindow *window, gint height); void gdk_window_raise (GdkWindow *window); void gdk_window_lower (GdkWindow *window); +void gdk_window_focus (GdkWindow *window, + guint32 timestamp); void gdk_window_set_user_data (GdkWindow *window, gpointer user_data); void gdk_window_set_override_redirect (GdkWindow *window, @@ -305,6 +309,8 @@ void gdk_window_merge_child_shapes (GdkWindow *window); gboolean gdk_window_is_visible (GdkWindow *window); gboolean gdk_window_is_viewable (GdkWindow *window); +GdkWindowState gdk_window_get_state (GdkWindow *window); + /* Set static bit gravity on the parent, and static * window gravity on all children. */ @@ -393,7 +399,13 @@ gboolean gdk_window_get_decorations (GdkWindow *window, void gdk_window_set_functions (GdkWindow *window, GdkWMFunction functions); GList * gdk_window_get_toplevels (void); + void gdk_window_iconify (GdkWindow *window); +void gdk_window_deiconify (GdkWindow *window); +void gdk_window_stick (GdkWindow *window); +void gdk_window_unstick (GdkWindow *window); +void gdk_window_maximize (GdkWindow *window); +void gdk_window_unmaximize (GdkWindow *window); void gdk_window_register_dnd (GdkWindow *window); diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c index a005e5a148..7056d9bc75 100644 --- a/gdk/x11/gdkevents-x11.c +++ b/gdk/x11/gdkevents-x11.c @@ -44,6 +44,8 @@ #include #endif +#include + typedef struct _GdkIOClosure GdkIOClosure; typedef struct _GdkEventPrivate GdkEventPrivate; @@ -119,7 +121,9 @@ static GSourceFuncs event_funcs = { NULL }; -GPollFD event_poll_fd; +static GPollFD event_poll_fd; + +static Window wmspec_check_window = None; /********************************************* * Functions for maintaining the event queue * @@ -260,6 +264,128 @@ gdk_add_client_message_filter (GdkAtom message_type, client_filters = g_list_prepend (client_filters, filter); } +static GdkAtom wm_state_atom = 0; +static GdkAtom wm_desktop_atom = 0; + +static void +gdk_check_wm_state_changed (GdkWindow *window) +{ + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + GdkAtom *atoms = NULL; + gulong i; + GdkAtom sticky_atom; + GdkAtom maxvert_atom; + GdkAtom maxhorz_atom; + gboolean found_sticky, found_maxvert, found_maxhorz; + GdkWindowState old_state; + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (wm_state_atom == 0) + wm_state_atom = gdk_atom_intern ("_NET_WM_STATE", FALSE); + + if (wm_desktop_atom == 0) + wm_desktop_atom = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE); + + XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window), + wm_state_atom, 0, G_MAXLONG, + False, XA_ATOM, &type, &format, &nitems, + &bytes_after, (guchar **)&atoms); + + if (type != None) + { + + sticky_atom = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE); + maxvert_atom = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE); + maxhorz_atom = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE); + + found_sticky = FALSE; + found_maxvert = FALSE; + found_maxhorz = FALSE; + + i = 0; + while (i < nitems) + { + if (atoms[i] == sticky_atom) + found_sticky = TRUE; + else if (atoms[i] == maxvert_atom) + found_maxvert = TRUE; + else if (atoms[i] == maxhorz_atom) + found_maxhorz = TRUE; + + ++i; + } + + XFree (atoms); + } + else + { + found_sticky = FALSE; + found_maxvert = FALSE; + found_maxhorz = FALSE; + } + + /* For found_sticky to remain TRUE, we have to also be on desktop + * 0xFFFFFFFF + */ + + if (found_sticky) + { + gulong *desktop; + + XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window), + wm_desktop_atom, 0, G_MAXLONG, + False, XA_CARDINAL, &type, &format, &nitems, + &bytes_after, (guchar **)&desktop); + + if (type != None) + { + if (*desktop != 0xFFFFFFFF) + found_sticky = FALSE; + XFree (desktop); + } + } + + old_state = gdk_window_get_state (window); + + if (old_state & GDK_WINDOW_STATE_STICKY) + { + if (!found_sticky) + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_STICKY, + 0); + } + else + { + if (found_sticky) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_STICKY); + } + + /* Our "maximized" means both vertical and horizontal; if only one, + * we don't expose that via GDK + */ + if (old_state & GDK_WINDOW_STATE_MAXIMIZED) + { + if (!(found_maxvert && found_maxhorz)) + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_MAXIMIZED, + 0); + } + else + { + if (found_maxvert && found_maxhorz) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_MAXIMIZED); + } +} + static gint gdk_event_translate (GdkEvent *event, XEvent *xevent, @@ -306,6 +432,19 @@ gdk_event_translate (GdkEvent *event, if (window != NULL) gdk_window_ref (window); + + if (wmspec_check_window != None && + xevent->xany.window == wmspec_check_window) + { + if (xevent->type == DestroyNotify) + wmspec_check_window = None; + + /* Eat events on this window unless someone had wrapped + * it as a foreign window + */ + if (window == NULL) + return FALSE; + } event->any.window = window; event->any.send_event = xevent->xany.send_event ? TRUE : FALSE; @@ -948,7 +1087,17 @@ gdk_event_translate (GdkEvent *event, xevent->xmap.window)); event->any.type = GDK_UNMAP; - event->any.window = window; + event->any.window = window; + + /* If we are shown (not withdrawn) and get an unmap, it means we + * were iconified in the X sense. If we are withdrawn, and get + * an unmap, it means we hid the window ourselves, so we + * will have already flipped the iconified bit off. + */ + if (GDK_WINDOW_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_ICONIFIED); if (gdk_xgrab_window == window_private) gdk_xgrab_window = NULL; @@ -962,6 +1111,12 @@ gdk_event_translate (GdkEvent *event, event->any.type = GDK_MAP; event->any.window = window; + + /* Unset iconified if it was set */ + if (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED) + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_ICONIFIED, + 0); break; @@ -1064,6 +1219,19 @@ gdk_event_translate (GdkEvent *event, event->property.atom = xevent->xproperty.atom; event->property.time = xevent->xproperty.time; event->property.state = xevent->xproperty.state; + + if (wm_state_atom == 0) + wm_state_atom = gdk_atom_intern ("_NET_WM_STATE", FALSE); + + if (wm_desktop_atom == 0) + wm_desktop_atom = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE); + + if (event->property.atom == wm_state_atom || + event->property.atom == wm_desktop_atom) + { + /* If window state changed, then synthesize those events. */ + gdk_check_wm_state_changed (event->property.window); + } break; @@ -1586,3 +1754,94 @@ gdk_x11_get_server_time (GdkWindow *window) return xevent.xproperty.time; } + +gboolean +gdk_wmspec_supported (GdkAtom property) +{ + static GdkAtom wmspec_check_atom = 0; + static GdkAtom wmspec_supported_atom = 0; + static GdkAtom *atoms = NULL; + static gulong n_atoms = 0; + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + Window *xwindow; + gulong i; + + if (wmspec_check_window != None) + { + if (atoms == NULL) + return FALSE; + + i = 0; + while (i < n_atoms) + { + if (atoms[i] == property) + return TRUE; + + ++i; + } + + return FALSE; + } + + if (atoms) + XFree (atoms); + + atoms = NULL; + n_atoms = 0; + + /* This function is very slow on every call if you are not running a + * spec-supporting WM. For now not optimized, because it isn't in + * any critical code paths, but if you used it somewhere that had to + * be fast you want to avoid "GTK is slow with old WMs" complaints. + * Probably at that point the function should be changed to query + * _NET_SUPPORTING_WM_CHECK only once every 10 seconds or something. + */ + + if (wmspec_check_atom == 0) + wmspec_check_atom = gdk_atom_intern ("_NET_SUPPORTING_WM_CHECK", FALSE); + + if (wmspec_supported_atom == 0) + wmspec_supported_atom = gdk_atom_intern ("_NET_SUPPORTED", FALSE); + + XGetWindowProperty (gdk_display, gdk_root_window, + wmspec_check_atom, 0, G_MAXLONG, + False, XA_WINDOW, &type, &format, &nitems, + &bytes_after, (guchar **)&xwindow); + + if (type != XA_WINDOW) + return FALSE; + + gdk_error_trap_push (); + + /* Find out if this WM goes away, so we can reset everything. */ + XSelectInput (gdk_display, *xwindow, + StructureNotifyMask); + + gdk_flush (); + + if (gdk_error_trap_pop ()) + { + XFree (xwindow); + return FALSE; + } + + XGetWindowProperty (gdk_display, gdk_root_window, + wmspec_supported_atom, 0, G_MAXLONG, + False, XA_ATOM, &type, &format, &n_atoms, + &bytes_after, (guchar **)&atoms); + + if (type != XA_ATOM) + return FALSE; + + wmspec_check_window = *xwindow; + XFree (xwindow); + + /* since wmspec_check_window != None this isn't infinite. ;-) */ + return gdk_wmspec_supported (property); +} + + + diff --git a/gdk/x11/gdkgeometry-x11.c b/gdk/x11/gdkgeometry-x11.c index 6102dc1ce5..17b6f8b9d5 100644 --- a/gdk/x11/gdkgeometry-x11.c +++ b/gdk/x11/gdkgeometry-x11.c @@ -28,6 +28,7 @@ #include "gdkprivate-x11.h" #include "gdkx.h" #include "gdkregion.h" +#include "gdkinternals.h" typedef struct _GdkWindowQueueItem GdkWindowQueueItem; typedef struct _GdkWindowParentPos GdkWindowParentPos; @@ -332,7 +333,7 @@ _gdk_window_move_resize_child (GdkWindow *window, if (impl->position_info.no_bg) gdk_window_tmp_reset_bg (window); - if (!impl->position_info.mapped && new_info.mapped && obj->mapped) + if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj)) XMapWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window)); impl->position_info = new_info; @@ -378,7 +379,7 @@ _gdk_window_move_resize_child (GdkWindow *window, if (impl->position_info.no_bg) gdk_window_tmp_reset_bg (window); - if (!impl->position_info.mapped && new_info.mapped && obj->mapped) + if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj)) XMapWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window)); impl->position_info = new_info; @@ -651,7 +652,7 @@ gdk_window_postmove (GdkWindow *window, new_info.x, new_info.y, new_info.width, new_info.height); } - if (!impl->position_info.mapped && new_info.mapped && obj->mapped) + if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj)) XMapWindow (GDK_DRAWABLE_XDISPLAY (window), GDK_DRAWABLE_XID (window)); if (impl->position_info.no_bg) diff --git a/gdk/x11/gdkmain-x11.c b/gdk/x11/gdkmain-x11.c index 1f0097b944..1f395e390d 100644 --- a/gdk/x11/gdkmain-x11.c +++ b/gdk/x11/gdkmain-x11.c @@ -737,4 +737,3 @@ gdk_send_xevent (Window window, gboolean propagate, glong event_mask, return result && !gdk_error_code; } - diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c index a68b2be56a..548bb587c4 100644 --- a/gdk/x11/gdkwindow-x11.c +++ b/gdk/x11/gdkwindow-x11.c @@ -635,7 +635,12 @@ gdk_window_foreign_new (GdkNativeWindow anid) impl->height = attrs.height; private->window_type = GDK_WINDOW_FOREIGN; private->destroyed = FALSE; - private->mapped = (attrs.map_state != IsUnmapped); + + if (attrs.map_state == IsUnmapped) + private->state = GDK_WINDOW_STATE_WITHDRAWN; + else + private->state = 0; + private->depth = attrs.depth; gdk_drawable_ref (window); @@ -709,6 +714,73 @@ gdk_window_destroy_notify (GdkWindow *window) gdk_drawable_unref (window); } +static void +set_initial_hints (GdkWindow *window) +{ + GdkWindowObject *private; + GdkAtom atoms[5]; + gint i; + + private = (GdkWindowObject*) window; + + if (private->state & GDK_WINDOW_STATE_ICONIFIED) + { + XWMHints *wm_hints; + + wm_hints = XGetWMHints (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window)); + if (!wm_hints) + wm_hints = XAllocWMHints (); + + wm_hints->flags |= StateHint; + wm_hints->initial_state = IconicState; + + XSetWMHints (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), wm_hints); + XFree (wm_hints); + } + + /* We set the spec hints regardless of whether the spec is supported, + * since it can't hurt and it's kind of expensive to check whether + * it's supported. + */ + + i = 0; + + if (private->state & GDK_WINDOW_STATE_MAXIMIZED) + { + atoms[i] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE); + ++i; + atoms[i] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE); + ++i; + } + + if (private->state & GDK_WINDOW_STATE_STICKY) + { + atoms[i] = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE); + ++i; + } + + if (i > 0) + { + XChangeProperty (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), + gdk_atom_intern ("_NET_WM_STATE", FALSE), + XA_ATOM, 32, PropModeReplace, + (guchar*) atoms, i); + } + + if (private->state & GDK_WINDOW_STATE_STICKY) + { + atoms[0] = 0xFFFFFFFF; + XChangeProperty (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), + gdk_atom_intern ("_NET_WM_DESKTOP", FALSE), + XA_CARDINAL, 32, PropModeReplace, + (guchar*) atoms, 1); + } +} + void gdk_window_show (GdkWindow *window) { @@ -719,13 +791,23 @@ gdk_window_show (GdkWindow *window) private = (GdkWindowObject*) window; if (!private->destroyed) { - private->mapped = TRUE; XRaiseWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window)); + + if (!GDK_WINDOW_IS_MAPPED (window)) + { + set_initial_hints (window); + + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_WITHDRAWN, + 0); + } + + g_assert (GDK_WINDOW_IS_MAPPED (window)); if (GDK_WINDOW_IMPL_X11 (private->impl)->position_info.mapped) - XMapWindow (GDK_WINDOW_XDISPLAY (window), - GDK_WINDOW_XID (window)); + XMapWindow (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window)); } } @@ -737,14 +819,36 @@ gdk_window_hide (GdkWindow *window) g_return_if_fail (window != NULL); private = (GdkWindowObject*) window; + + /* You can't simply unmap toplevel windows. */ + switch (private->window_type) + { + case GDK_WINDOW_TOPLEVEL: + case GDK_WINDOW_DIALOG: + case GDK_WINDOW_TEMP: /* ? */ + gdk_window_withdraw (window); + return; + break; + + case GDK_WINDOW_FOREIGN: + case GDK_WINDOW_ROOT: + case GDK_WINDOW_CHILD: + break; + } + if (!private->destroyed) { - private->mapped = FALSE; + if (GDK_WINDOW_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_WITHDRAWN); + g_assert (!GDK_WINDOW_IS_MAPPED (window)); + _gdk_window_clear_update_area (window); XUnmapWindow (GDK_WINDOW_XDISPLAY (window), - GDK_WINDOW_XID (window)); + GDK_WINDOW_XID (window)); } } @@ -757,8 +861,17 @@ gdk_window_withdraw (GdkWindow *window) private = (GdkWindowObject*) window; if (!private->destroyed) - XWithdrawWindow (GDK_WINDOW_XDISPLAY (window), - GDK_WINDOW_XID (window), 0); + { + if (GDK_WINDOW_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_WITHDRAWN); + + g_assert (!GDK_WINDOW_IS_MAPPED (window)); + + XWithdrawWindow (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), 0); + } } void @@ -944,6 +1057,41 @@ gdk_window_lower (GdkWindow *window) XLowerWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window)); } +void +gdk_window_focus (GdkWindow *window, + guint32 timestamp) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (gdk_wmspec_supported (gdk_atom_intern ("_NET_ACTIVE_WINDOW", FALSE))) + { + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_ACTIVE_WINDOW", FALSE); + xev.xclient.format = 32; + xev.xclient.data.l[0] = 0; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + } + else + { + XSetInputFocus (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW (window), + RevertToNone, + timestamp); + } +} + void gdk_window_set_hints (GdkWindow *window, gint x, @@ -1807,12 +1955,43 @@ gdk_window_set_icon_name (GdkWindow *window, GUINT_TO_POINTER (TRUE)); set_text_property (window, gdk_atom_intern ("WM_ICON_NAME", FALSE), name); -} +} void gdk_window_iconify (GdkWindow *window) { Display *display; + GdkWindowObject *private; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + display = GDK_WINDOW_XDISPLAY (window); + + private = (GdkWindowObject*) window; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + XIconifyWindow (display, GDK_WINDOW_XWINDOW (window), DefaultScreen (display)); + + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_ICONIFIED); + } +} + +void +gdk_window_deiconify (GdkWindow *window) +{ + Display *display; + GdkWindowObject *private; g_return_if_fail (window != NULL); g_return_if_fail (GDK_IS_WINDOW (window)); @@ -1821,7 +2000,221 @@ gdk_window_iconify (GdkWindow *window) return; display = GDK_WINDOW_XDISPLAY (window); - XIconifyWindow (display, GDK_WINDOW_XWINDOW (window), DefaultScreen (display)); + + private = (GdkWindowObject*) window; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + gdk_window_show (window); + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_ICONIFIED, + 0); + } +} + +void +gdk_window_stick (GdkWindow *window) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + /* "stick" means stick to all desktops _and_ do not scroll with the + * viewport. i.e. glue to the monitor glass in all cases. + */ + + XEvent xev; + + /* Request stick during viewport scroll */ + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_ADD", FALSE); + xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE); + xev.xclient.data.l[2] = 0; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + + /* Request desktop 0xFFFFFFFF */ + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = 0xFFFFFFFF; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_STICKY); + } +} + +void +gdk_window_unstick (GdkWindow *window) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + XEvent xev; + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + gulong *current_desktop; + + /* Request unstick from viewport */ + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_REMOVE", FALSE); + xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE); + xev.xclient.data.l[2] = 0; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + + /* Get current desktop, then set it; this is a race, but not + * one that matters much in practice. + */ + XGetWindowProperty (gdk_display, gdk_root_window, + gdk_atom_intern ("_NET_CURRENT_DESKTOP", FALSE), + 0, G_MAXLONG, + False, XA_CARDINAL, &type, &format, &nitems, + &bytes_after, (guchar **)¤t_desktop); + + if (type == XA_CARDINAL) + { + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = *current_desktop; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + + XFree (current_desktop); + } + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_STICKY, + 0); + + } +} + +void +gdk_window_maximize (GdkWindow *window) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_ADD", FALSE); + xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE); + xev.xclient.data.l[2] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE); + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + } + else + { + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_MAXIMIZED); + } +} + +void +gdk_window_unmaximize (GdkWindow *window) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_REMOVE", FALSE); + xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE); + xev.xclient.data.l[2] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE); + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + } + else + { + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_MAXIMIZED, + 0); + } } void diff --git a/gdk/x11/gdkx.h b/gdk/x11/gdkx.h index 336bd35f31..628d32206f 100644 --- a/gdk/x11/gdkx.h +++ b/gdk/x11/gdkx.h @@ -185,6 +185,9 @@ gpointer gdk_xid_table_lookup (XID xid); guint32 gdk_x11_get_server_time (GdkWindow *window); +/* returns TRUE if we support the given WM spec feature */ +gboolean gdk_wmspec_supported (GdkAtom property); + #define gdk_window_lookup(xid) ((GdkWindow*) gdk_xid_table_lookup (xid)) #define gdk_pixmap_lookup(xid) ((GdkPixmap*) gdk_xid_table_lookup (xid)) #define gdk_font_lookup(xid) ((GdkFont*) gdk_xid_table_lookup (xid)) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index d680f97ee9..0689427e5a 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -888,6 +888,7 @@ gtk_main_do_event (GdkEvent *event) case GDK_SELECTION_NOTIFY: case GDK_CLIENT_EVENT: case GDK_VISIBILITY_NOTIFY: + case GDK_WINDOW_STATE: gtk_widget_event (event_widget, event); break; diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index e9fee7463d..852be31e07 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -99,6 +99,7 @@ enum { CLIENT_EVENT, NO_EXPOSE_EVENT, VISIBILITY_NOTIFY_EVENT, + WINDOW_STATE_EVENT, DEBUG_MSG, LAST_SIGNAL }; @@ -314,6 +315,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) klass->focus_out_event = NULL; klass->map_event = NULL; klass->unmap_event = NULL; + klass->window_state_event = NULL; klass->property_notify_event = gtk_selection_property_notify; klass->selection_clear_event = gtk_selection_clear; klass->selection_request_event = gtk_selection_request; @@ -775,6 +777,14 @@ gtk_widget_class_init (GtkWidgetClass *klass) gtk_marshal_BOOLEAN__POINTER, GTK_TYPE_BOOL, 1, GTK_TYPE_GDK_EVENT); + widget_signals[WINDOW_STATE_EVENT] = + gtk_signal_new ("window_state_event", + GTK_RUN_LAST, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GtkWidgetClass, no_expose_event), + gtk_marshal_BOOLEAN__POINTER, + GTK_TYPE_BOOL, 1, + GTK_TYPE_GDK_EVENT); widget_signals[DEBUG_MSG] = gtk_signal_new ("debug_msg", GTK_RUN_LAST | GTK_RUN_ACTION, @@ -2337,6 +2347,9 @@ gtk_widget_event (GtkWidget *widget, case GDK_UNMAP: signal_num = UNMAP_EVENT; break; + case GDK_WINDOW_STATE: + signal_num = WINDOW_STATE_EVENT; + break; case GDK_PROPERTY_NOTIFY: signal_num = PROPERTY_NOTIFY_EVENT; break; diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 527132673a..c512a8991f 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -325,7 +325,9 @@ struct _GtkWidgetClass GdkEventClient *event); gint (* no_expose_event) (GtkWidget *widget, GdkEventAny *event); - + gint (* window_state_event) (GtkWidget *widget, + GdkEventWindowState *event); + /* selection */ void (* selection_get) (GtkWidget *widget, GtkSelectionData *selection_data, diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 3d87ca2527..9b204245e8 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -1247,7 +1247,8 @@ static void gtk_window_map (GtkWidget *widget) { GtkWindow *window; - + GdkWindow *toplevel; + g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_WINDOW (widget)); @@ -1260,7 +1261,28 @@ gtk_window_map (GtkWidget *widget) !GTK_WIDGET_MAPPED (window->bin.child)) gtk_widget_map (window->bin.child); + if (window->frame) + toplevel = window->frame; + else + toplevel = widget->window; + + if (window->maximize_initially) + gdk_window_maximize (toplevel); + else + gdk_window_unmaximize (toplevel); + + if (window->stick_initially) + gdk_window_stick (toplevel); + else + gdk_window_unstick (toplevel); + + if (window->iconify_initially) + gdk_window_iconify (toplevel); + else + gdk_window_deiconify (toplevel); + gdk_window_show (widget->window); + if (window->frame) gdk_window_show (window->frame); } @@ -1398,7 +1420,8 @@ gtk_window_realize (GtkWidget *widget) gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); if (window->frame) gtk_style_set_background (widget->style, window->frame, GTK_STATE_NORMAL); - + + /* This is a bad hack to set the window background. */ gtk_window_paint (widget, NULL); if (window->transient_parent && @@ -1407,7 +1430,6 @@ gtk_window_realize (GtkWidget *widget) GTK_WIDGET (window->transient_parent)->window); } - static void gtk_window_unrealize (GtkWidget *widget) { @@ -2786,3 +2808,278 @@ gtk_window_set_frame_dimensions (GtkWindow *window, } + +/** + * gtk_window_present: + * @window: a #GtkWindow + * + * Presents a window to the user. This may mean raising the window + * in the stacking order, deiconifying it, moving it to the current + * desktop, and/or giving it the keyboard focus, possibly dependent + * on the user's platform, window manager, and preferences. + * + * If @window is hidden, this function calls gtk_widget_show() + * as well. + * + * This function should be used when the user tries to open a window + * that's already open. Say for example the preferences dialog is + * currently open, and the user chooses Preferences from the menu + * a second time; use gtk_window_present() to move the already-open dialog + * where the user can see it. + * + **/ +void +gtk_window_present (GtkWindow *window) +{ + GtkWidget *widget; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + widget = GTK_WIDGET (window); + + if (GTK_WIDGET_VISIBLE (window)) + { + g_assert (widget->window != NULL); + + gdk_window_show (widget->window); + + /* note that gdk_window_focus() will also move the window to + * the current desktop, for WM spec compliant window managers. + */ + gdk_window_focus (widget->window, + gtk_get_current_event_time ()); + } + else + { + gtk_widget_show (widget); + } +} + +/** + * gtk_window_iconify: + * @window: a #GtkWindow + * + * Asks to iconify @window. Note that you shouldn't assume the window + * is definitely iconified afterward, because other entities (e.g. the + * user or window manager) could deiconify it again, or there may not + * be a window manager in which case iconification isn't possible, + * etc. But normally the window will end up iconified. Just don't write + * code that crashes if not. + * + * It's permitted to call this function before showing a window, + * in which case the window will be iconified before it ever appears + * onscreen. + * + * You can track iconification via the "window_state_event" signal + * on #GtkWidget. + * + **/ +void +gtk_window_iconify (GtkWindow *window) +{ + GtkWidget *widget; + GdkWindow *toplevel; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + widget = GTK_WIDGET (window); + + window->iconify_initially = TRUE; + + if (window->frame) + toplevel = window->frame; + else + toplevel = widget->window; + + if (toplevel != NULL) + gdk_window_iconify (toplevel); +} + +/** + * gtk_window_deiconify: + * @window: a #GtkWindow + * + * Asks to deiconify @window. Note that you shouldn't assume the + * window is definitely deiconified afterward, because other entities + * (e.g. the user or window manager) could iconify it again before + * your code which assumes deiconification gets to run. + * + * You can track iconification via the "window_state_event" signal + * on #GtkWidget. + **/ +void +gtk_window_deiconify (GtkWindow *window) +{ + GtkWidget *widget; + GdkWindow *toplevel; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + widget = GTK_WIDGET (window); + + window->iconify_initially = FALSE; + + if (window->frame) + toplevel = window->frame; + else + toplevel = widget->window; + + if (toplevel != NULL) + gdk_window_deiconify (toplevel); +} + +/** + * gtk_window_stick: + * @window: a #GtkWindow + * + * Asks to stick @window, which means that it will appear on all user + * desktops. Note that you shouldn't assume the window is definitely + * stuck afterward, because other entities (e.g. the user or window + * manager) could unstick it again, and some window managers do not + * support sticking windows. But normally the window will end up + * stuck. Just don't write code that crashes if not. + * + * It's permitted to call this function before showing a window. + * + * You can track stickiness via the "window_state_event" signal + * on #GtkWidget. + * + **/ +void +gtk_window_stick (GtkWindow *window) +{ + GtkWidget *widget; + GdkWindow *toplevel; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + widget = GTK_WIDGET (window); + + window->stick_initially = TRUE; + + if (window->frame) + toplevel = window->frame; + else + toplevel = widget->window; + + if (toplevel != NULL) + gdk_window_stick (toplevel); +} + +/** + * gtk_window_unstick: + * @window: a #GtkWindow + * + * Asks to unstick @window, which means that it will appear on only + * one of the user's desktops. Note that you shouldn't assume the + * window is definitely unstuck afterward, because other entities + * (e.g. the user or window manager) could stick it again. But + * normally the window will end up stuck. Just don't write code that + * crashes if not. + * + * You can track stickiness via the "window_state_event" signal + * on #GtkWidget. + * + **/ +void +gtk_window_unstick (GtkWindow *window) +{ + GtkWidget *widget; + GdkWindow *toplevel; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + widget = GTK_WIDGET (window); + + window->stick_initially = FALSE; + + if (window->frame) + toplevel = window->frame; + else + toplevel = widget->window; + + if (toplevel != NULL) + gdk_window_unstick (toplevel); +} + +/** + * gtk_window_maximize: + * @window: a #GtkWindow + * + * Asks to maximize @window, so that it becomes full-screen. Note that + * you shouldn't assume the window is definitely maximized afterward, + * because other entities (e.g. the user or window manager) could + * unmaximize it again, and not all window managers support + * maximization. But normally the window will end up maximized. Just + * don't write code that crashes if not. + * + * It's permitted to call this function before showing a window, + * in which case the window will be maximized when it appears onscreen + * initially. + * + * You can track maximization via the "window_state_event" signal + * on #GtkWidget. + * + **/ +void +gtk_window_maximize (GtkWindow *window) +{ + GtkWidget *widget; + GdkWindow *toplevel; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + widget = GTK_WIDGET (window); + + window->maximize_initially = TRUE; + + if (window->frame) + toplevel = window->frame; + else + toplevel = widget->window; + + if (toplevel != NULL) + gdk_window_maximize (toplevel); +} + +/** + * gtk_window_unmaximize: + * @window: a #GtkWindow + * + * Asks to unmaximize @window. Note that you shouldn't assume the + * window is definitely unmaximized afterward, because other entities + * (e.g. the user or window manager) could maximize it again, and not + * all window managers honor requests to unmaximize. But normally the + * window will end up unmaximized. Just don't write code that crashes + * if not. + * + * You can track maximization via the "window_state_event" signal + * on #GtkWidget. + * + **/ +void +gtk_window_unmaximize (GtkWindow *window) +{ + GtkWidget *widget; + GdkWindow *toplevel; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + widget = GTK_WIDGET (window); + + window->maximize_initially = FALSE; + + if (window->frame) + toplevel = window->frame; + else + toplevel = widget->window; + + if (toplevel != NULL) + gdk_window_unmaximize (toplevel); +} + + + + + + diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h index 7d4cb6b0e8..51be63fca8 100644 --- a/gtk/gtkwindow.h +++ b/gtk/gtkwindow.h @@ -85,6 +85,11 @@ struct _GtkWindow guint has_frame : 1; + /* gtk_window_iconify() called before realization */ + guint iconify_initially : 1; + guint stick_initially : 1; + guint maximize_initially : 1; + guint frame_left; guint frame_top; guint frame_right; @@ -153,6 +158,14 @@ GList* gtk_window_list_toplevels (void); /* Get the "built-in" accel group (convenience thing) */ GtkAccelGroup* gtk_window_get_default_accel_group (GtkWindow *window); +void gtk_window_present (GtkWindow *window); +void gtk_window_iconify (GtkWindow *window); +void gtk_window_deiconify (GtkWindow *window); +void gtk_window_stick (GtkWindow *window); +void gtk_window_unstick (GtkWindow *window); +void gtk_window_maximize (GtkWindow *window); +void gtk_window_unmaximize (GtkWindow *window); + /* --- internal functions --- */ void gtk_window_set_focus (GtkWindow *window, diff --git a/gtk/testgtk.c b/gtk/testgtk.c index b2477ad28f..00704342e7 100644 --- a/gtk/testgtk.c +++ b/gtk/testgtk.c @@ -7663,6 +7663,199 @@ create_wmhints (void) gtk_widget_destroy (window); } + +/* + * Window state tracking + */ + +static gint +window_state_callback (GtkWidget *widget, + GdkEventWindowState *event, + gpointer data) +{ + GtkWidget *label = data; + gchar *msg; + + msg = g_strconcat (GTK_WINDOW (widget)->title, ": ", + (event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ? + "withdrawn" : "not withdrawn", ", ", + (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ? + "iconified" : "not iconified", ", ", + (event->new_window_state & GDK_WINDOW_STATE_STICKY) ? + "sticky" : "not sticky", ", ", + (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) ? + "maximized" : "not maximized", + NULL); + + gtk_label_set_text (GTK_LABEL (label), msg); + + g_free (msg); + + return FALSE; +} + +static GtkWidget* +tracking_label (GtkWidget *window) +{ + GtkWidget *label; + GtkWidget *hbox; + GtkWidget *button; + + hbox = gtk_hbox_new (FALSE, 5); + + gtk_signal_connect_object (GTK_OBJECT (hbox), + "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT (window)); + + label = gtk_label_new (""); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + gtk_signal_connect (GTK_OBJECT (window), + "window_state_event", + GTK_SIGNAL_FUNC (window_state_callback), + label); + + button = gtk_button_new_with_label ("Deiconify"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_deiconify), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Iconify"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_iconify), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Present"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_present), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Show"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_widget_show), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + + return hbox; +} + +static GtkWidget* +get_state_controls (GtkWidget *window) +{ + GtkWidget *vbox; + GtkWidget *button; + + vbox = gtk_vbox_new (FALSE, 0); + + button = gtk_button_new_with_label ("Stick"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_stick), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Unstick"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_unstick), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Maximize"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_maximize), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Unmaximize"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_unmaximize), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Iconify"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_iconify), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Hide (withdraw)"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_widget_hide), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + gtk_widget_show_all (vbox); + + return vbox; +} + +void +create_window_states (void) +{ + static GtkWidget *window = NULL; + GtkWidget *label; + GtkWidget *box1; + GtkWidget *iconified; + GtkWidget *normal; + GtkWidget *controls; + + if (!window) + { + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &window); + + gtk_window_set_title (GTK_WINDOW (window), "Window states"); + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + + iconified = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_iconify (iconified); + gtk_window_set_title (GTK_WINDOW (iconified), "Iconified initially"); + controls = get_state_controls (iconified); + gtk_container_add (GTK_CONTAINER (iconified), controls); + + normal = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (normal), "Deiconified initially"); + controls = get_state_controls (normal); + gtk_container_add (GTK_CONTAINER (normal), controls); + + label = tracking_label (iconified); + gtk_container_add (GTK_CONTAINER (box1), label); + + label = tracking_label (normal); + gtk_container_add (GTK_CONTAINER (box1), label); + + gtk_widget_show_all (iconified); + gtk_widget_show_all (normal); + gtk_widget_show_all (box1); + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show (window); + else + gtk_widget_destroy (window); +} + /* * GtkProgressBar */ @@ -9361,6 +9554,7 @@ create_main_window (void) { "tooltips", create_tooltips }, { "tree", create_tree_mode_window}, { "WM hints", create_wmhints }, + { "window states", create_window_states } }; int nbuttons = sizeof (buttons) / sizeof (buttons[0]); GtkWidget *window; diff --git a/tests/testgtk.c b/tests/testgtk.c index b2477ad28f..00704342e7 100644 --- a/tests/testgtk.c +++ b/tests/testgtk.c @@ -7663,6 +7663,199 @@ create_wmhints (void) gtk_widget_destroy (window); } + +/* + * Window state tracking + */ + +static gint +window_state_callback (GtkWidget *widget, + GdkEventWindowState *event, + gpointer data) +{ + GtkWidget *label = data; + gchar *msg; + + msg = g_strconcat (GTK_WINDOW (widget)->title, ": ", + (event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ? + "withdrawn" : "not withdrawn", ", ", + (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ? + "iconified" : "not iconified", ", ", + (event->new_window_state & GDK_WINDOW_STATE_STICKY) ? + "sticky" : "not sticky", ", ", + (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) ? + "maximized" : "not maximized", + NULL); + + gtk_label_set_text (GTK_LABEL (label), msg); + + g_free (msg); + + return FALSE; +} + +static GtkWidget* +tracking_label (GtkWidget *window) +{ + GtkWidget *label; + GtkWidget *hbox; + GtkWidget *button; + + hbox = gtk_hbox_new (FALSE, 5); + + gtk_signal_connect_object (GTK_OBJECT (hbox), + "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT (window)); + + label = gtk_label_new (""); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + gtk_signal_connect (GTK_OBJECT (window), + "window_state_event", + GTK_SIGNAL_FUNC (window_state_callback), + label); + + button = gtk_button_new_with_label ("Deiconify"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_deiconify), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Iconify"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_iconify), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Present"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_present), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Show"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_widget_show), + GTK_OBJECT (window)); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + + return hbox; +} + +static GtkWidget* +get_state_controls (GtkWidget *window) +{ + GtkWidget *vbox; + GtkWidget *button; + + vbox = gtk_vbox_new (FALSE, 0); + + button = gtk_button_new_with_label ("Stick"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_stick), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Unstick"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_unstick), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Maximize"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_maximize), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Unmaximize"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_unmaximize), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Iconify"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_window_iconify), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + button = gtk_button_new_with_label ("Hide (withdraw)"); + gtk_signal_connect_object (GTK_WIDGET (button), + "clicked", + GTK_SIGNAL_FUNC (gtk_widget_hide), + GTK_OBJECT (window)); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + + gtk_widget_show_all (vbox); + + return vbox; +} + +void +create_window_states (void) +{ + static GtkWidget *window = NULL; + GtkWidget *label; + GtkWidget *box1; + GtkWidget *iconified; + GtkWidget *normal; + GtkWidget *controls; + + if (!window) + { + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &window); + + gtk_window_set_title (GTK_WINDOW (window), "Window states"); + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + + iconified = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_iconify (iconified); + gtk_window_set_title (GTK_WINDOW (iconified), "Iconified initially"); + controls = get_state_controls (iconified); + gtk_container_add (GTK_CONTAINER (iconified), controls); + + normal = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (normal), "Deiconified initially"); + controls = get_state_controls (normal); + gtk_container_add (GTK_CONTAINER (normal), controls); + + label = tracking_label (iconified); + gtk_container_add (GTK_CONTAINER (box1), label); + + label = tracking_label (normal); + gtk_container_add (GTK_CONTAINER (box1), label); + + gtk_widget_show_all (iconified); + gtk_widget_show_all (normal); + gtk_widget_show_all (box1); + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show (window); + else + gtk_widget_destroy (window); +} + /* * GtkProgressBar */ @@ -9361,6 +9554,7 @@ create_main_window (void) { "tooltips", create_tooltips }, { "tree", create_tree_mode_window}, { "WM hints", create_wmhints }, + { "window states", create_window_states } }; int nbuttons = sizeof (buttons) / sizeof (buttons[0]); GtkWidget *window; -- 2.30.2